package com.zzhua.config;

import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.SessionListener;
import org.apache.shiro.session.mgt.SessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.session.mgt.WebSessionKey;
import org.apache.shiro.web.util.WebUtils;
import org.crazycake.shiro.IRedisManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.util.StringUtils;

import javax.servlet.Filter;
import javax.servlet.http.HttpServletRequest;
import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;

@Configuration
public class ShiroConfiguration {

    @Value("${spring.redis.host:127.0.0.1}")
    private String host;

    @Value("${spring.redis.port:6379}")
    private int port;

    @Value("${spring.redis.timeout:6379}")
    private int timeout;

    @Bean
    public Realm realm() {
        return new MyRealm();
    }

    @Bean
    public IRedisManager redisManager() {
        RedisManager redisManager = new RedisManager();
        redisManager.setHost(host);
        redisManager.setPort(port);
        redisManager.setTimeout(timeout);
        return redisManager;
    }

    @Bean
    @Primary
    public RedisSessionDAO redisSessionDAO(IRedisManager redisManager) {
        RedisSessionDAO redisSessionDAO = new RedisSessionDAO();
        redisSessionDAO.setRedisManager(redisManager);
        return redisSessionDAO;
    }

    // 自定义会话管理器
    @Bean
    public DefaultWebSessionManager sessionManager(RedisSessionDAO redisSessionDAO,
                                                   @Autowired(required = false) SessionListener sessionListener) {
        DefaultWebSessionManager sessionManager = new DefaultWebSessionManager() {

            private static final String HEADER = "Authorization";

            /**
             * 此方法会被调用2次，
             *        第一次是: 请求刚进来，正在resolveContextSession(subjectContext)
             *        第二次是: 使用session存东西setAttribute(key,val),
             *                                DelegatingSession会使用保存的sessionManager
             *                                                 并且会把key给传过来
             *
             * @param key
             * @return
             */
            @Override
            public Serializable getSessionId(SessionKey key) {
                if (WebUtils.isWeb(key)) {
                    WebSessionKey webSessionKey = (WebSessionKey) key;
                    HttpServletRequest req = (HttpServletRequest) webSessionKey.getServletRequest();
                    String header = req.getHeader(HEADER);
                    return StringUtils.isEmpty(header)?key.getSessionId():header;
                }
                // return super.getSessionId(key)
                // 返回key的sessionId（不支持父类其它获取sessionId的方式,只允许请求头方式）,
                // 但是一定不能返回为null
                return key.getSessionId();
            }
        };
        // 即不会给客户端写回sessionId
        sessionManager.setSessionIdCookieEnabled(false);
        // 即不会重写url路径（如果浏览器不支持cookie,JSESSIONID拼接在url上）
        sessionManager.setSessionIdUrlRewritingEnabled(false);
        // 设置sessionDao
        sessionManager.setSessionDAO(redisSessionDAO);
        // 每隔1分钟检测一次session
        sessionManager.setSessionValidationInterval(60000);
        // 设置自定义的对应的会话监听器
        sessionManager.setSessionListeners(Arrays.asList(sessionListener));
        return sessionManager;
    }

    @Bean
    public SecurityManager securityManager(Realm realm, SessionManager sessionManager) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        securityManager.setRealm(realm);
        securityManager.setSessionManager(sessionManager);
        return securityManager;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
        ShiroFilterFactoryBean shiroFilter = new ShiroFilterFactoryBean();
        // 注册自定义filter
        HashMap<String, Filter> filters = new HashMap<>();
        // 在springboot中,不能把这个filter交给spring管理，
        // 否则它会直接被嵌入到web，有可能先于shiroFilter执行，而导致不能拿到Subject
        MyFilter myFilter = new MyFilter();
        filters.put("myFilter", myFilter);
        shiroFilter.setFilters(filters);
        LinkedHashMap<String, String> chainDefMap = new LinkedHashMap<>();
        chainDefMap.put("/free", "anon"); // 匿名filter
        chainDefMap.put("/login", "anon"); // 匿名filter
        chainDefMap.put("/**", "myFilter"); // 自定义filter
        // 设置拦截器链
        shiroFilter.setFilterChainDefinitionMap(chainDefMap);
        // 设置安全管理器
        shiroFilter.setSecurityManager(securityManager);
        return shiroFilter;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor advisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        return advisor;
    }



}
